﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using JasRibbonApplicatie.Classes;

namespace JasRibbonApplicatie.Controllers
{
	public class OntleningBeheerder
	{

		public Aanvraag HuidigeAanvraag { get; set; }
		public Magazijnier HuidigeMagazijnier { get; set; }

		public List<OntleningData<TypeMateriaalMetToebehoren>> OntleningDataMaterialen { get; set; }
		public List<OntleningData<toebehorentypes>> OntleningDataToebehoren { get; set; }

		public List<String> lijstScancodes;


		// alle materialen en toebehoren worden opgehaald bij een aanvraag;
		// daarna ook de reeds ontleende materialen (die eventueel ook al binnengebracht zijn)....
		public OntleningBeheerder(Aanvraag aanvraag, Magazijnier magazijnier)
		{
			// controleren of aanvraag = null? :: voorkomen van programmeerfouten in de toekomst...? (andere volgorde van actief zetten van controls levert reeds problemen op
			// => beter dat misschien aanpakken
			this.HuidigeAanvraag = aanvraag;
			this.HuidigeMagazijnier = magazijnier;

			this.OntleningDataMaterialen = new List<OntleningData<TypeMateriaalMetToebehoren>>();
			this.OntleningDataToebehoren = new List<OntleningData<toebehorentypes>>();

			// aanvraag: bestelde materialen verwerken
			List<MateriaalBestelling> materiaalbestellingen = Queries.GetMateriaalBestellingenByAanvraagId(HuidigeAanvraag.AanvraagId);
			foreach (var bestelling in materiaalbestellingen)
			{             
				TypeMateriaalMetToebehoren materiaaltype = bestelling.MateriaalVolledig;
				int index = this.GetIndexProducttypeAndAddIfNeeded<TypeMateriaalMetToebehoren>(materiaaltype, OntleningDataMaterialen);

				OntleningDataMaterialen.ElementAt(index).AantalGoedgekeurd += bestelling.AantalGoedgekeurd;


                //Dictionary<toebehorentypes, int> lijstToebehoren = materiaaltype.LijstToebehorenTypes;
                List<toebehorentypes> lijstToebehoren = materiaaltype.LijstToebehorenTypes;
                //foreach (var toebehorenKeyValuePair in lijstToebehoren)
                //{
                //    index = this.GetIndexProducttypeAndAddIfNeeded<toebehorentypes>(toebehorenKeyValuePair.Key, OntleningDataToebehoren);
                //    // bvb: luidspreker heeft 3 kabels:     2 luidsprekers goedgekeurd => 6 kabels toevoegen = (2 * 3)
                //    OntleningDataToebehoren.ElementAt(index).AantalGoedgekeurd += (bestelling.AantalGoedgekeurd * toebehorenKeyValuePair.Value);
                //}
                // het kan zijn dat eenzelfde toebehoren meerdere keren voorkomt in de lijst (bvb. 2 kabels nodig...)
                foreach (var typeToebehoren in lijstToebehoren)
                {
                    index = this.GetIndexProducttypeAndAddIfNeeded<toebehorentypes>(typeToebehoren, OntleningDataToebehoren);
                    OntleningDataToebehoren.ElementAt(index).AantalGoedgekeurd += 1;
                }
			}
			// bestellingen afgehandeld

			// reeds uitgeleende producten ophalen (de producten die terug binnengebracht zijn worden momenteel niet opgehaald
			List<ontleningen> lijstOntleningen = Queries.GetOntleendeProductenByAanvraagId(this.HuidigeAanvraag.AanvraagId);
			lijstScancodes = new List<string>();
			foreach (var ontlening in lijstOntleningen) // er wordt vanuitgegaan dat voor alle ontleningen ook een aanvraag met materiaalbestelling is....
			{
                // anders is deze scancode reeds weer verdwenen
				if (ontlening.tijdstip_inlevering.Equals(DateTime.MinValue))
				{
					lijstScancodes.Add(ontlening.streepjescode);
				}
				
				char eersteKarakter = ontlening.streepjescode.ToLower()[0];
				bool reedsTerugBinnen = (!(ontlening.tijdstip_inlevering.Equals(DateTime.MinValue)));
				if (eersteKarakter.Equals('m'))
				{
					TypeMateriaalMetToebehoren materiaaltype = Queries.GetTypeMateriaalMetToebehorenByMateriaalScancode(ontlening.streepjescode);
					this.ontleningenInstellenUitDatabase<TypeMateriaalMetToebehoren>(OntleningDataMaterialen, materiaaltype, reedsTerugBinnen);
				}
				else if (eersteKarakter.Equals('t'))
				{
					toebehorentypes toebehorentype = Queries.GetTypeToebehorenByToebehorenScancode(ontlening.streepjescode);
					this.ontleningenInstellenUitDatabase<toebehorentypes>(OntleningDataToebehoren, toebehorentype, reedsTerugBinnen);
				}
			}
		}


		// return: 'false' wanneer de ontlening niet mag doorgaan!
		// throws ArgumentException wanneer het product niet in de database gevonden kan worden, of wanneer
		// het aantalOntleningen het aantalGoedgekeurd bereikt heeft...
		public bool OntleningToevoegen(String productScancode)
		{
			String code = productScancode.ToLower();
			bool gevonden = false;
			if (!lijstScancodes.Contains(code)) // controleren of product niet reeds ingescand is
			{
				if (code.First().Equals('m')) // materiaal
				{
					TypeMateriaalMetToebehoren producttype = Queries.GetTypeMateriaalMetToebehorenByMateriaalScancode(code);
					gevonden = OntleningGeorganiseerdToevoegen<TypeMateriaalMetToebehoren>(producttype, OntleningDataMaterialen);
				}
				else if (code.First().Equals('t')) // toebehoren
				{
					toebehorentypes typeToebehoren = Queries.GetTypeToebehorenByToebehorenScancode(code);
					gevonden = OntleningGeorganiseerdToevoegen<toebehorentypes>(typeToebehoren, OntleningDataToebehoren);
				}
				else
					throw new ArgumentException("Er dient een geldige productcode ingescand te worden!");

				if (gevonden)
				{
                    if (Queries.IsProductNietUitgeleendMomenteel(code))
                    {
                        lijstScancodes.Add(code);
                        Queries.AddOntlening(this.HuidigeAanvraag.AanvraagId, code, this.HuidigeMagazijnier.MagazijnierId);
                        return true;
                    }
                    else throw new ArgumentException("Product is momenteel reeds uitgeleend!");
				}
			}
			else
			{
				throw new ArgumentException("Product is reeds uitgescand...");
			}
			return false;
		}

		// return false indien producttype niet gevonden in ontlening, of indien dit soort materiaal reeds te veel keer
		// uitgeleend is (aantalOntleend >= aantalGoedgekeurd)
		public bool OntleningGeorganiseerdToevoegen<X>(X producttype, List<OntleningData<X>> lijstProducttypes)
		{
			bool gevonden = false;
			if (producttype != null)
				{
					int i = 0;
					while ((i < lijstProducttypes.Count) && (!gevonden))
					{
						if (lijstProducttypes.ElementAt(i).Producttype.Equals(producttype))
							gevonden = true;
						else
							i++;
					}
					if (gevonden)
						if (lijstProducttypes.ElementAt(i).AantalOntleend < lijstProducttypes.ElementAt(i).AantalGoedgekeurd)
						{
							lijstProducttypes.ElementAt(i).AantalOntleend += 1;
						}
						else
							throw new ArgumentException("Producttype kan niet (meer) uitgeleend worden - zie AantalGoedgekeurd...");                       
				}
				else
					throw new ArgumentException("Ingescande productcode kan niet in de database gevonden worden...");
			return gevonden;
		}

		public bool OntleningBeeindigen(String productScancode)
		{
			if (lijstScancodes.Contains(productScancode))
			{
                // dan is het sowieso een geldige scancode
                if (productScancode.ToLower().First().Equals('m'))
                {
                    TypeMateriaalMetToebehoren producttype = Queries.GetTypeMateriaalMetToebehorenByMateriaalScancode(productScancode);
                    int index = GetIndexProducttypeAndAddIfNeeded<TypeMateriaalMetToebehoren>(producttype, OntleningDataMaterialen);
                    OntleningDataMaterialen[index].AantalBinnengebracht += 1;
                }
                else
                {
                    toebehorentypes producttype = Queries.GetTypeToebehorenByToebehorenScancode(productScancode);
                    int index = GetIndexProducttypeAndAddIfNeeded<toebehorentypes>(producttype, OntleningDataToebehoren);
                    OntleningDataToebehoren[index].AantalBinnengebracht += 1;
                }
                Queries.BeeindigOntlening(this.HuidigeAanvraag.AanvraagId, productScancode, this.HuidigeMagazijnier.MagazijnierId);
                lijstScancodes.Remove(productScancode); // mag niet nogmaals beeindigd worden
			}
			else
			{
                // deze error nog uit elkaar trekken
				throw new ArgumentException("Dit product bestaat niet, is niet uitgescand geweest voor deze aanvraag, of is reeds terug ingeleverd!");
			}
            return true;
		}

		private int GetIndexProducttypeAndAddIfNeeded<X>(X producttype, List<OntleningData<X>> lijstOntleningData)
		{
			bool gevonden = false;
			int index = 0;
			while ((index < lijstOntleningData.Count) && (!gevonden))
			{
				if (lijstOntleningData.ElementAt(index).Producttype.Equals(producttype))
				{
					gevonden = true;
				}
				else
					index++;
			}
			// als producttype niet gevonden is, moet dit producttype nog toegevoegd worden aan de lijst...
			if (!gevonden)
			{
				lijstOntleningData.Add(new OntleningData<X>(producttype));
				index = lijstOntleningData.Count - 1; // want de index is gevraagd
			}
			return index;
		}

		private void ontleningenInstellenUitDatabase<X>(List<OntleningData<X>> lijstOntleningData, X producttype, bool reedsTerugBinnen)
		{
			int i = 0;
			bool gevonden = false;
			while ((i < lijstOntleningData.Count) && (!gevonden))
			{
				if (lijstOntleningData.ElementAt(i).Producttype.Equals(producttype))
				{
					lijstOntleningData.ElementAt(i).AantalOntleend += 1;
					if (reedsTerugBinnen)
						lijstOntleningData.ElementAt(i).AantalBinnengebracht += 1;
				}
				i++;
			}
		}

        public void RemoveOntlening(String scancode)
        {
            bool codeBestaat = false;
            if (scancode.ToLower().First().Equals('m')) // materiaal
            {
                TypeMateriaalMetToebehoren producttype = Queries.GetTypeMateriaalMetToebehorenByMateriaalScancode(scancode);
                int index = GetIndexProducttypeAndAddIfNeeded<TypeMateriaalMetToebehoren>(producttype, OntleningDataMaterialen);
                OntleningDataMaterialen[index].AantalOntleend -= 1;
                codeBestaat = true;
            }
            else if (scancode.ToLower().First().Equals('t')) // toebehoren
            {
                toebehorentypes typeToebehoren = Queries.GetTypeToebehorenByToebehorenScancode(scancode);
                int index = GetIndexProducttypeAndAddIfNeeded<toebehorentypes>(typeToebehoren, OntleningDataToebehoren);
                OntleningDataToebehoren[index].AantalOntleend -= 1;
                codeBestaat = true;
            }
            if (codeBestaat)
            {
                Queries.UndoAddOntlening(this.HuidigeAanvraag.AanvraagId, scancode);
                // ontlening verwijderen uit database
                lijstScancodes.Remove(scancode);
            }

        }

        public void UndoBeeindigingOntlening(String scancode)
        {
            bool codebestaat = false;
            if (scancode.ToLower().First().Equals('m')) // materiaal
            {
                TypeMateriaalMetToebehoren producttype = Queries.GetTypeMateriaalMetToebehorenByMateriaalScancode(scancode);
                int index = GetIndexProducttypeAndAddIfNeeded<TypeMateriaalMetToebehoren>(producttype, OntleningDataMaterialen);
                OntleningDataMaterialen[index].AantalBinnengebracht -= 1;
                codebestaat = true;
            }
            else if (scancode.ToLower().First().Equals('t')) // toebehoren
            {
                toebehorentypes typeToebehoren = Queries.GetTypeToebehorenByToebehorenScancode(scancode);
                int index = GetIndexProducttypeAndAddIfNeeded<toebehorentypes>(typeToebehoren, OntleningDataToebehoren);
                OntleningDataToebehoren[index].AantalBinnengebracht -= 1;
                codebestaat = true;
            }
            if (codebestaat)
            {
                Queries.UndoBeeindigingOntlening(this.HuidigeAanvraag.AanvraagId, scancode);
                // magazijnier en tijdstip terug op beginwaarde instellen
                lijstScancodes.Add(scancode); // is dan dus weer uitgeleend op dat moment
            }

        }
	}
}
